Programming with Plots

Plots as Objects

Elizabeth King
Kevin Middleton

This week

  1. Plots as objects
  2. Creating figures from multiple plots
  3. Dynamic plot creation
  4. Processing lists of plots

Objects in R

Everything in R is an object with one or more classes.

  • Tibbles are objects
  • Functions are objects
  • Plots are objects
set.seed(87453)
M <- tibble(x = rnorm(100))
p <- ggplot(M, aes(x)) +
  geom_density()
class(p)
[1] "gg"     "ggplot"
str(p)

Objects in R

List of 9
 $ data       : tibble [100 × 1] (S3: tbl_df/tbl/data.frame)
  ..$ x: num [1:100] -0.78 0.623 -0.297 0.503 0.147 ...
 $ layers     :List of 1
  ..$ :Classes 'LayerInstance', 'Layer', 'ggproto', 'gg' <ggproto object: Class LayerInstance, Layer, gg>
    aes_params: list
    compute_aesthetics: function
    compute_geom_1: function
    compute_geom_2: function
    compute_position: function
    compute_statistic: function
    computed_geom_params: NULL
    computed_mapping: NULL
    computed_stat_params: NULL
    constructor: call
    data: waiver
    draw_geom: function
    finish_statistics: function
    geom: <ggproto object: Class GeomDensity, GeomArea, GeomRibbon, Geom, gg>
        aesthetics: function
        default_aes: list
        draw_group: function
        draw_key: function
        draw_layer: function
        draw_panel: function
        extra_params: na.rm orientation
        handle_na: function
        non_missing_aes: 
        optional_aes: 
        parameters: function
        rename_size: TRUE
        required_aes: x y
        setup_data: function
        setup_params: function
        use_defaults: function
        super:  <ggproto object: Class GeomArea, GeomRibbon, Geom, gg>
    geom_params: list
    inherit.aes: TRUE
    layer_data: function
    map_statistic: function
    mapping: NULL
    position: <ggproto object: Class PositionIdentity, Position, gg>
        compute_layer: function
        compute_panel: function
        required_aes: 
        setup_data: function
        setup_params: function
        super:  <ggproto object: Class Position, gg>
    print: function
    setup_layer: function
    show.legend: NA
    stat: <ggproto object: Class StatDensity, Stat, gg>
        aesthetics: function
        compute_group: function
        compute_layer: function
        compute_panel: function
        default_aes: uneval
        dropped_aes: weight
        extra_params: na.rm orientation
        finish_layer: function
        non_missing_aes: 
        optional_aes: 
        parameters: function
        required_aes: x|y
        retransform: TRUE
        setup_data: function
        setup_params: function
        super:  <ggproto object: Class Stat, gg>
    stat_params: list
    super:  <ggproto object: Class Layer, gg> 
 $ scales     :Classes 'ScalesList', 'ggproto', 'gg' <ggproto object: Class ScalesList, gg>
    add: function
    clone: function
    find: function
    get_scales: function
    has_scale: function
    input: function
    n: function
    non_position_scales: function
    scales: list
    super:  <ggproto object: Class ScalesList, gg> 
 $ mapping    :List of 1
  ..$ x: language ~x
  .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..- attr(*, "class")= chr "uneval"
 $ theme      : list()
 $ coordinates:Classes 'CoordCartesian', 'Coord', 'ggproto', 'gg' <ggproto object: Class CoordCartesian, Coord, gg>
    aspect: function
    backtransform_range: function
    clip: on
    default: TRUE
    distance: function
    expand: TRUE
    is_free: function
    is_linear: function
    labels: function
    limits: list
    modify_scales: function
    range: function
    render_axis_h: function
    render_axis_v: function
    render_bg: function
    render_fg: function
    setup_data: function
    setup_layout: function
    setup_panel_guides: function
    setup_panel_params: function
    setup_params: function
    train_panel_guides: function
    transform: function
    super:  <ggproto object: Class CoordCartesian, Coord, gg> 
 $ facet      :Classes 'FacetNull', 'Facet', 'ggproto', 'gg' <ggproto object: Class FacetNull, Facet, gg>
    compute_layout: function
    draw_back: function
    draw_front: function
    draw_labels: function
    draw_panels: function
    finish_data: function
    init_scales: function
    map_data: function
    params: list
    setup_data: function
    setup_params: function
    shrink: TRUE
    train_scales: function
    vars: function
    super:  <ggproto object: Class FacetNull, Facet, gg> 
 $ plot_env   :<environment: R_GlobalEnv> 
 $ labels     :List of 4
  ..$ x     : chr "x"
  ..$ y     : chr "density"
  .. ..- attr(*, "fallback")= logi TRUE
  ..$ fill  : chr "fill"
  .. ..- attr(*, "fallback")= logi TRUE
  ..$ weight: chr "weight"
  .. ..- attr(*, "fallback")= logi TRUE
 - attr(*, "class")= chr [1:2] "gg" "ggplot"

Accessing elements of plots

p$labels$x
[1] "x"
p$data
# A tibble: 100 × 1
         x
     <dbl>
 1 -0.780 
 2  0.623 
 3 -0.297 
 4  0.503 
 5  0.147 
 6 -0.419 
 7 -0.353 
 8  2.48  
 9 -0.0539
10 -0.437 
# … with 90 more rows

All of the data is saved with a plot

Modifying plots the hard way

p$labels$x <- "This is the x axis"
p$labels$y <- "This is the y axis"
p

Modifying plots the easy way

p <- p +
  labs(x = "This is the x axis", y = "This is the y axis")
p

Adding, removing, modifying plot elements

  • +ing to plot objects allows you to add or overwrite any plot element
    • R warns you when you overwrite
    • Warnings can be quieted
  • Use element_blank() to remove elements

Plot grids

patchwork package

p2 <- ggplot(M, aes(x)) +
  geom_dotplot()
p + p2

Plot grids

Plots within plots: inset_element()

p2 +
  inset_element(p, 0.6, 0.6, 1, 1)

Plots within plots: inset_element()

Zooming in: ggforce::facet_zoom()

ggplot() +
  geom_point(data = palmerpenguins::penguins |> drop_na(),
             aes(x = body_mass_g, y = bill_length_mm, color = species)) +
  scale_color_paletteer_d(`"feathers::cassowary"`, guide = NULL) +
  facet_zoom(x = body_mass_g > 4000 & body_mass_g < 5000)

Zooming in: ggforce::facet_zoom()

Saving plots as objects

  • Plots like any R object can be saved
  • Prefer .Rds over .Rda (e.g., from save())
    • readr::write_rds() or saveRDS()
    • Rds does not save the object name as part of the file
  • Good for batch processing (Lewis HPC)
write_rds(x = p, file = "Plot.Rds")
old_plot <- read_rds(file = "Plot.Rds)